package service

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"time"

	"gitee.com/war2001/NiuMaEasyGo/global"
	"gitee.com/war2001/NiuMaEasyGo/model"
	"gitee.com/war2001/NiuMaEasyGo/nm_core/storage"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// FileServiceImpl 文件服务实现
type FileServiceImpl struct {
	db      *gorm.DB
	storage *storage.Manager
}

// NewFileService 创建文件服务
func NewFileService(db *gorm.DB, storageManager *storage.Manager) model.FileService {
	return &FileServiceImpl{
		db:      db,
		storage: storageManager,
	}
}

// Upload 上传文件
func (s *FileServiceImpl) Upload(file *model.File, data []byte) error {
	if file == nil {
		return errors.New("文件信息不能为空")
	}

	if len(data) == 0 {
		return errors.New("文件内容不能为空")
	}

	// 创建上下文
	ctx := context.Background()

	// 创建上传选项
	option := &storage.UploadOption{
		Path:        file.Category, // 使用文件分类作为存储路径
		ContentType: file.ContentType,
		IsPublic:    file.IsPublic,
		Metadata: map[string]string{
			"user_id":   fmt.Sprintf("%d", file.UserID),
			"tenant_id": fmt.Sprintf("%d", file.TenantID),
			"category":  file.Category,
			"tags":      file.Tags,
			"remark":    file.Remark,
		},
	}

	// 上传文件
	fileInfo, err := s.storage.Upload(ctx, file.Name, bytes.NewReader(data), option)
	if err != nil {
		return fmt.Errorf("上传文件失败: %w", err)
	}

	// 更新文件信息
	file.Path = fileInfo.Path
	file.URL = fileInfo.URL
	file.Size = fileInfo.Size
	file.ContentType = fileInfo.ContentType
	file.UploadTime = time.Now()
	file.Storage = string(s.storage.GetStorageType())

	// 保存到数据库
	if err := s.db.Create(file).Error; err != nil {
		// 如果保存到数据库失败，尝试删除已上传的文件
		_ = s.storage.Delete(ctx, fileInfo.Path)
		return fmt.Errorf("保存文件信息失败: %w", err)
	}

	return nil
}

// GetByID 获取文件信息
func (s *FileServiceImpl) GetByID(id uint) (*model.File, error) {
	var file model.File
	if err := s.db.First(&file, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, fmt.Errorf("文件不存在: %w", err)
		}
		return nil, fmt.Errorf("获取文件信息失败: %w", err)
	}
	return &file, nil
}

// List 获取文件列表
func (s *FileServiceImpl) List(page, pageSize int, query map[string]interface{}) ([]*model.File, int64, error) {
	var files []*model.File
	var total int64

	db := s.db.Model(&model.File{})

	// 构建查询条件
	if query != nil {
		if name, ok := query["name"].(string); ok && name != "" {
			db = db.Where("name LIKE ?", "%"+name+"%")
		}
		if category, ok := query["category"].(string); ok && category != "" {
			db = db.Where("category = ?", category)
		}
		if tags, ok := query["tags"].(string); ok && tags != "" {
			db = db.Where("tags LIKE ?", "%"+tags+"%")
		}
		if userID, ok := query["user_id"].(uint); ok && userID > 0 {
			db = db.Where("user_id = ?", userID)
		}
		if tenantID, ok := query["tenant_id"].(uint); ok && tenantID > 0 {
			db = db.Where("tenant_id = ?", tenantID)
		}
		if storage, ok := query["storage"].(string); ok && storage != "" {
			db = db.Where("storage = ?", storage)
		}
		if isPublic, ok := query["is_public"].(bool); ok {
			db = db.Where("is_public = ?", isPublic)
		}
		if startTime, ok := query["start_time"].(time.Time); ok {
			db = db.Where("upload_time >= ?", startTime)
		}
		if endTime, ok := query["end_time"].(time.Time); ok {
			db = db.Where("upload_time <= ?", endTime)
		}
	}

	// 获取总数
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, fmt.Errorf("获取文件总数失败: %w", err)
	}

	// 获取分页数据
	if err := db.Offset((page - 1) * pageSize).Limit(pageSize).
		Order("upload_time DESC").Find(&files).Error; err != nil {
		return nil, 0, fmt.Errorf("获取文件列表失败: %w", err)
	}

	return files, total, nil
}

// Delete 删除文件
func (s *FileServiceImpl) Delete(id uint) error {
	// 获取文件信息
	file, err := s.GetByID(id)
	if err != nil {
		return err
	}

	// 创建上下文
	ctx := context.Background()

	// 删除存储中的文件
	if err := s.storage.Delete(ctx, file.Path); err != nil {
		zap.L().Error("删除存储中的文件失败", zap.Error(err), zap.String("path", file.Path))
		// 继续执行，删除数据库记录
	}

	// 删除数据库记录
	if err := s.db.Delete(&model.File{}, id).Error; err != nil {
		return fmt.Errorf("删除文件记录失败: %w", err)
	}

	return nil
}

// BatchDelete 批量删除文件
func (s *FileServiceImpl) BatchDelete(ids []uint) error {
	if len(ids) == 0 {
		return errors.New("文件ID列表不能为空")
	}

	// 获取文件列表
	var files []*model.File
	if err := s.db.Where("id IN ?", ids).Find(&files).Error; err != nil {
		return fmt.Errorf("获取文件列表失败: %w", err)
	}

	// 创建上下文
	ctx := context.Background()

	// 批量删除存储中的文件
	for _, file := range files {
		if err := s.storage.Delete(ctx, file.Path); err != nil {
			zap.L().Error("删除存储中的文件失败", zap.Error(err), zap.String("path", file.Path))
			// 继续执行，删除数据库记录
		}
	}

	// 批量删除数据库记录
	if err := s.db.Delete(&model.File{}, "id IN ?", ids).Error; err != nil {
		return fmt.Errorf("批量删除文件记录失败: %w", err)
	}

	return nil
}

// GetURL 获取文件访问URL
func (s *FileServiceImpl) GetURL(id uint, expiration time.Duration) (string, error) {
	// 获取文件信息
	file, err := s.GetByID(id)
	if err != nil {
		return "", err
	}

	// 如果文件是公开的，直接返回URL
	if file.IsPublic {
		return file.URL, nil
	}

	// 创建上下文
	ctx := context.Background()

	// 获取临时URL
	url, err := s.storage.GetURL(ctx, file.Path, expiration)
	if err != nil {
		return "", fmt.Errorf("获取文件访问URL失败: %w", err)
	}

	return url, nil
}

// GetContent 获取文件内容
func (s *FileServiceImpl) GetContent(id uint) ([]byte, error) {
	// 获取文件信息
	file, err := s.GetByID(id)
	if err != nil {
		return nil, err
	}

	// 创建上下文
	ctx := context.Background()

	// 获取文件URL
	url, err := s.storage.GetURL(ctx, file.Path, 5*time.Minute)
	if err != nil {
		return nil, fmt.Errorf("获取文件访问URL失败: %w", err)
	}

	// 下载文件内容
	resp, err := global.HttpClient.Get(url)
	if err != nil {
		return nil, fmt.Errorf("下载文件内容失败: %w", err)
	}
	defer resp.Body.Close()

	// 读取文件内容
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取文件内容失败: %w", err)
	}

	return data, nil
}
